
/* 
  1. promise需要传入一个执行器，执行器会立即执行
  2. promise有三种状态 pending  fulfilled rejected  
  3. 三种状态只能从pending 到fulfilled或者rejected 不可逆
  4. then 方法接受两个可选参数一个成功回调一个失败回调并返回一个promise对象
  5. then 方法可以链式调用 
  5. finally 方法不管成功还是失败都会调用的一个方法不在意状态
  6. 私有的resolve方法 会将传入的参数包装成一个promise对象并返回出来
  7. all 方法将传入的数组的每一个值都过滤一下并等待其中的异步方法返回再返回值的集合
  8. catch方法用then方法实现传入一个失败的回调  
*/
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  constructor(exce) {
    try {
      exce(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }
  status = PENDING
  value = undefined
  reason = undefined
  successCallBack = []
  failCallBack = []
  resolve = value => {
    //改变状态 执行成功回调
    if (this.status != PENDING) return
    this.value = value
    this.status = FULFILLED
    //执行回调
    while (this.successCallBack.length) this.successCallBack.shift()(this.value)
  }
  reject = reason => {
    //改变状态执行失败回调
    if (this.status != PENDING) return
    this.reason = reason
    this.status = REJECTED
    //执行回调
    while (this.failCallBack.length) this.failCallBack.shift()(reason)
  }
  then(successCallBack, failCallBack) {
    //可选参数 
    successCallBack = successCallBack ? successCallBack : value => value
    failCallBack = failCallBack ? failCallBack : reason => { throw reason }
    //返回promise 对象 
    let promise2 = new MyPromise((resolve, reject) => {
      //当前promise对象的状态判断 
      if (this.status === FULFILLED) {
        //成功状态  使用定时器是因为需要用到promise2 所以改为异步
        setTimeout(() => {
          try {
            let t = successCallBack(this.value)
            // 参数类型判断  返回自身报错  普通值直接调用
            resolvePromise(promise2, t, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      } else if (this.status === REJECTED) {
        // 失败状态  使用定时器是因为需要用到promise2 所以改为异步
        setTimeout(() => {
          try {
            let t = failCallBack(this.reason)
            // 参数类型判断  返回自身报错  普通值直接调用
            resolvePromise(promise2, t, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      } else {
        // 等待状态
        this.successCallBack.push(() => {
          //成功状态  使用定时器是因为需要用到promise2 所以改为异步
          setTimeout(() => {
            try {
              let t = successCallBack(this.value)
              // 参数类型判断  返回自身报错  普通值直接调用
              resolvePromise(promise2, t, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
        this.failCallBack.push(() => {
          //失败状态  使用定时器是因为需要用到promise2 所以改为异步
          setTimeout(() => {
            try {
              let t = failCallBack(this.reason)
              // 参数类型判断  返回自身报错  普通值直接调用
              resolvePromise(promise2, t, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
      }
    })
    return promise2
  }
  finally(callBack) {
    return this.then(resolve => {
      return MyPromise.resolve(callBack()).then(() => resolve)
    },
      reason => {
        return MyPromise.resolve(callBack()).then(() => reason)
      })
  }
  catch(failCallBack) {
    return this.then(undefined, failCallBack)
  }
  static all(array) {
    let result = [] // 新的promise对象回调里面的参数
    let num = 0 //用来判断array异步是否走完
    return new MyPromise((resolve, reject) => {
      function resultAdd(value, key) {
        result[key] = value
        num++
        if (array.length === num) {
          resolve(result)
        }
      }
      for (let index = 0; index < array.length; index++) {
        let curry = array[index]
        if (curry instanceof MyPromise) {
          curry.then(resolve => resultAdd(resolve, index), (reason) => reject(reason))
        } else {
          resultAdd(curry, index)
        }
      }
    })
  }
  static resolve(value) {
    //判断传入的值是promise对象还是普通值
    // console.log(value, 'resolve.......')
    // return value instanceof MyPromise ? value : new MyPromise((resolve, reject) => resolve(value))
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value));
  }
}

function resolvePromise(promise2, t, resolve, reject) {
  if (promise2 === t) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  } else if (t instanceof MyPromise) {
    t.then(resolve, reject)
  } else {
    resolve(t)
  }
}
module.exports = MyPromise